import {isObject, isArray} from '../utils/utils'
import {arrayMethods} from './arrary'
import {Dep} from './dep'

class Observer {
  constructor(value) {
    this.dep = new Dep()
    Object.defineProperty(value, '__ob__', {
      value: this,
      enumerable: false
    })
    Object.defineProperty(value, 'dep', {
      enumerable: false
    })
    if (isArray(value)) {
      /*
       * 1.每一个对象都有一个__proto__指向所属类的原型 fn.__proto__===Function.prototype
       * 2.每一个原型上都有一个constructor指向函数本身 Function.prototype.constructor ===Function*/
      value.__proto__ = arrayMethods
      this.observeArray(value)
    } else {
      this.walk(value)
    }
  }
  walk(data) {
    Object.keys(data).forEach((key) => {
      defineReactive(data, key, data[key])
    })
  }
  observeArray(data) {
    data.forEach((item) => observe(item))
  }
}
/*
 * vue2使用defineProperty需要一开始加载就进行递归操作,所以耗性能,层次越深越耗新能
 * 优化原则
 * 1.不要把所有数据都放到date中
 * 2.date数据不要写太深,尽量扁平化数据
 * 3.不要频繁获取数据
 * 4.如果数据不需要响应式,可以Object.freeze冻结数据*/
function defineReactive(data, key, value) {
  const childOb = observe(value)
  let dep = new Dep()
  Object.defineProperty(data, key, {
    get() {
      dep.depend()
      if(childOb){
        childOb.dep.depend()
        if (Array.isArray(value)) {
          dependArray(value)
        }
      }
      return value
    },
    set(newValue) {
      if (newValue === value) return
      //如果是对象就再次劫持
      observe(newValue)
      value = newValue
      dep.notify()
    }
  })
}
function dependArray (value) {
  for (let e, i = 0, l = value.length; i < l; i++) {
    e = value[i]
    e && e.__ob__ && e.__ob__.dep.depend()
    if (Array.isArray(e)) {
      dependArray(e)
    }
  }
}
export function observe(value) {
  if (!isObject(value)) return
  return new Observer(value)
}
